package encode;

import gui.GUI;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;

import structure.Area;
import structure.AreasComparator;
import structure.ImageContainer;
import structure.RLEBuffer;
import structure.RLECompressedFile;
import structure.RLEPCXBuffer;
import structure.Sequence;
import structure.Square;
import structure.VSeries;
import utilities.rle2dUtilities;

public class ImageServices implements Runnable{
	private ImageContainer ic;
	
	private ArrayList<Area> redAreas;
	private ArrayList<Area> blueAreas;
	private ArrayList<Area> greenAreas;
	
	private ArrayList<Area> redMarkedSquares;
	private ArrayList<Area> blueMarkedSquares;
	private ArrayList<Area> greenMarkedSquares;
	
	private ArrayList<Area> redMarkedVSeries;
	private ArrayList<Area> blueMarkedVSeries;
	private ArrayList<Area> greenMarkedVSeries;
	
	private ArrayList<Byte> redSequence;
	private ArrayList<Byte> blueSequence;
	private ArrayList<Byte> greenSequence;
	
	private int byteSize;
	
	
	private BufferedImage bimage;
	private GUI gui;
	private boolean isFinished = false;
	
	private byte [][] redBitmap;
	private byte [][] blueBitmap;
	private byte [][] greenBitmap;
	
	private int redSeriesCount;
	private int greenSeriesCount;
	private int blueSeriesCount;
	
	private int priorityType;
	private int rleType;
	
	private int[] redPcxMap;
	private int[] greenPcxMap;
	private int[] bluePcxMap;
	
	private HuffmanEncode encoder;
	private boolean useSeparateStatistics;
	//private int min
	
	private RLECompressedFile rleFile = null;
	private File inputFile;
	
	public ImageServices(BufferedImage bImage,GUI gui, int blockPriorityType, int rleType, boolean useSeparateStatistics, File inputFile) {
		
		this.priorityType = blockPriorityType;
		this.rleType = rleType;
		
		ic = new ImageContainer(bImage);
		
		redAreas = new ArrayList<Area>();
		blueAreas = new ArrayList<Area>();
		greenAreas = new ArrayList<Area>();
		
		redBitmap = new byte[ic.getHeigth()][ic.getWidth()];
		blueBitmap = new byte[ic.getHeigth()][ic.getWidth()];
		greenBitmap = new byte[ic.getHeigth()][ic.getWidth()];
		
		redSequence = new ArrayList<Byte>();
		blueSequence = new ArrayList<Byte>();
		greenSequence = new ArrayList<Byte>();
		
		redMarkedSquares = new ArrayList<Area>();
		blueMarkedSquares = new ArrayList<Area>();
		greenMarkedSquares = new ArrayList<Area>();
		
		redMarkedVSeries = new ArrayList<Area>();
		blueMarkedVSeries = new ArrayList<Area>();
		greenMarkedVSeries = new ArrayList<Area>();
		
		redSeriesCount = 0;
		greenSeriesCount = 0;
		blueSeriesCount = 0;
		
		setGui(gui);
		
		setSquerMinSize(ic.getWidth(), ic.getHeigth());
		
		setVSeriesMinLen(ic.getWidth(), ic.getHeigth());
		
		setByteSize(ic.getWidth(),ic.getHeigth());
		
		this.useSeparateStatistics = useSeparateStatistics;
		
		this.inputFile = inputFile;
		
		new Thread(this).start();
	}
	
	public ImageContainer getIc() {
		return ic;
	}
	public void setIc(ImageContainer ic) {
		this.ic = ic;
	}
	
	public ArrayList<Area> getRedAreas() {
		return redAreas;
	}

	public void setRedAreas(ArrayList<Area> redAreas) {
		this.redAreas = redAreas;
	}

	public ArrayList<Area> getBlueAreas() {
		return blueAreas;
	}

	public void setBlueAreas(ArrayList<Area> blueAreas) {
		this.blueAreas = blueAreas;
	}

	public ArrayList<Area> getGreenAreas() {
		return greenAreas;
	}

	public void setGreenAreas(ArrayList<Area> greenAreas) {
		this.greenAreas = greenAreas;
	}
	
	public ArrayList<Byte> getRedSequence() {
		return redSequence;
	}

	public void setRedSequence(ArrayList<Byte> redSequence) {
		this.redSequence = redSequence;
	}

	public ArrayList<Byte> getBlueSequence() {
		return blueSequence;
	}

	public void setBlueSequence(ArrayList<Byte> blueSequence) {
		this.blueSequence = blueSequence;
	}

	public ArrayList<Byte> getGreenSequence() {
		return greenSequence;
	}

	public void setGreenSequence(ArrayList<Byte> greenSequence) {
		this.greenSequence = greenSequence;
	}


	public ArrayList<Area> getRedMarkedSquares() {
		return redMarkedSquares;
	}

	public void setRedMarkedSquares(ArrayList<Area> redMarkedSquares) {
		this.redMarkedSquares = redMarkedSquares;
	}

	public ArrayList<Area> getBlueMarkedSquares() {
		return blueMarkedSquares;
	}

	public void setBlueMarkedSquares(ArrayList<Area> blueMarkedSquares) {
		this.blueMarkedSquares = blueMarkedSquares;
	}

	public ArrayList<Area> getGreenMarkedSquares() {
		return greenMarkedSquares;
	}

	public void setGreenMarkedSquares(ArrayList<Area> greenMarkedSquares) {
		this.greenMarkedSquares = greenMarkedSquares;
	}

	public ArrayList<Area> getRedMarkedVSeries() {
		return redMarkedVSeries;
	}

	public void setRedMarkedVSeries(ArrayList<Area> redMarkedVSeries) {
		this.redMarkedVSeries = redMarkedVSeries;
	}

	public ArrayList<Area> getBlueMarkedVSeries() {
		return blueMarkedVSeries;
	}

	public void setBlueMarkedVSeries(ArrayList<Area> blueMarkedVSeries) {
		this.blueMarkedVSeries = blueMarkedVSeries;
	}

	public ArrayList<Area> getGreenMarkedVSeries() {
		return greenMarkedVSeries;
	}

	public void setGreenMarkedVSeries(ArrayList<Area> greenMarkedVSeries) {
		this.greenMarkedVSeries = greenMarkedVSeries;
	}

	public GUI getGui() {
		return gui;
	}

	public void setGui(GUI gui) {
		this.gui = gui;
	}
	

	public int getByteSize() {
		return byteSize;
	}

	public void setByteSize(int byteSize) {
		this.byteSize = byteSize;
	}
	
	public void setByteSize(int width,int heigth) {
		int max = (Math.max(width, heigth));
		int size = 1;
		
		if(max>255)
			size++;
		
		setByteSize(size);
	}

	public void setSquerMinSize(int width,int heigth) {
		int max = (Math.max(width, heigth));
		int size = 1;
		
		if(max>255)
			size++;
		
		size = 2*size+2;	// 2 zmienne na x i y oraz 2 bity na rozmiwar i kolor
		
		rle2dUtilities.SQUARE_MIN = size;
	}
	
	
	public void setVSeriesMinLen(int width,int heigth) {
		int max = (Math.max(width, heigth));
		
		int size = 1;
		
		if(max>255)
			size++;
		
		size = 2*size+2;	// 2 zmienne na x i y oraz 2 bity na rozmiwar i kolor
		rle2dUtilities.VSERIES_MIN_LEN = size;
	}

	
	public int getMarkerValue(ImageContainer ic,String COLOR_NAME) {
		int [] probability = new int[256];
		int val = 0;
		for(int i=0;i<ic.getHeigth();i++) {
			for(int j=0;j<ic.getWidth();j++){
				if(rle2dUtilities.RED_NAME.equals(COLOR_NAME))
					val = ic.getRedAt(j, i);
				else if(rle2dUtilities.GREEN_NAME.equals(COLOR_NAME))
					val = ic.getGreenAt(j, i);
				else if(rle2dUtilities.BLUE_NAME.equals(COLOR_NAME))
					val = ic.getBlueAt(j, i);
				probability[val]++;			
			}
		}
		
		int min = probability[0];
		int marker = 0;
		for(int i=1;i<probability.length;i++){
			if (probability[i]<min) {
				min = probability[i];
				marker = i;
			}
		}
		
		return marker;
	}

	/**
	 * 
	 * @param colorName
	 */
	public void scanMatrix (String colorName) {
		String COLOR_NAME = colorName;
		int actualProgress = getGui().getProgress();
		if (rle2dUtilities.RED_NAME.equals(colorName)) {
			rle2dUtilities.H_SERIES_RED_MARKER = getMarkerValue(ic,rle2dUtilities.RED_NAME);
		}
		else if (rle2dUtilities.BLUE_NAME.equals(colorName)) {
			rle2dUtilities.H_SERIES_BLUE_MARKER = getMarkerValue(ic,rle2dUtilities.BLUE_NAME);
		}
		else if (rle2dUtilities.GREEN_NAME.equals(colorName)) {
			rle2dUtilities.H_SERIES_GREEN_MARKER = getMarkerValue(ic,rle2dUtilities.GREEN_NAME);
		}
		
		for (int y=0;y<ic.getHeigth();y++) {
			for (int x=0;x<ic.getWidth();) {
				getGui().setProgress(actualProgress+(int)((25/3)*(y*ic.getHeigth()+x))/(ic.getHeigth()*ic.getWidth()));
				Integer nextPos = findSquare(ic, x, y, COLOR_NAME);
				if (nextPos != null)
					x = nextPos.intValue();
				else
					x++;
			}
		}
		
		for (int x=0;x<ic.getWidth();x++) {
			for(int y=0;y<ic.getHeigth();) {
				Integer nextPos = findVSeries(ic, x, y, COLOR_NAME);
				getGui().setProgress(actualProgress+(int)((25/3)*(y*ic.getWidth()+x))/(ic.getHeigth()*ic.getWidth()));
				if (nextPos != null)
					y = nextPos.intValue();
				else
					y++;
			}
		}
	}
	
	/***********************************************************
	 * 
	 * @param matrix
	 * @param x
	 * @param y
	 * @param color
	 * @param rowC
	 * @return zwraca null jezli nie ma pola blokujacego rozrost, w p.p. zwraca wspolrzedna x blokujacego pola
	 */
	
	public Integer getBlockingField (ImageContainer ic, int x,int y,int color,int size,String COLOR_NAME){
		
		int dimX = x+size;
		int dimY = y+size;
		
		if (size >= rle2dUtilities.SQUARE_MAX)
			return new Integer(dimX);
		
		if(dimX >= ic.getWidth() && dimY >= ic.getHeigth())
			return new Integer(dimX);
		
		for (int i=x;i<dimX;i++) {	//tutaj tylko mniejsze bo inaczej zachodza na siebie 2 przypadki w rogu
			
			int curr_color = -1;
			if (i<ic.getWidth() && dimY<ic.getHeigth()) {
				if(rle2dUtilities.RED_NAME.equals(COLOR_NAME))
					curr_color = ic.getRedAt(i, dimY);
				else if(rle2dUtilities.GREEN_NAME.equals(COLOR_NAME))
					curr_color = ic.getGreenAt(i, dimY);
				else if(rle2dUtilities.BLUE_NAME.equals(COLOR_NAME))
					curr_color = ic.getBlueAt(i, dimY);
			}
			else if (i>ic.getWidth() && dimY>ic.getHeigth())
				return new Integer(i+1);
			else
				curr_color = color;
			if (curr_color!= color)
				return new Integer(i+1);
		}
		
		for (int i=y;i<=dimY;i++) {
			int curr_color =-1;
			if(dimX<ic.getWidth() && i<ic.getHeigth()) {
				if(rle2dUtilities.RED_NAME.equals(COLOR_NAME))
					curr_color = ic.getRedAt(dimX,i);
				else if(rle2dUtilities.GREEN_NAME.equals(COLOR_NAME))
					curr_color = ic.getGreenAt(dimX,i);
				else if(rle2dUtilities.BLUE_NAME.equals(COLOR_NAME))
					curr_color = ic.getBlueAt(dimX,i);
			}
			else if (dimX>ic.getWidth() && i>ic.getHeigth())
				return new Integer(dimX);
			else
				curr_color = color;
			
			if (curr_color != color)
				return new Integer(dimX);
		}
		
		return null;
	}
	
	/************************************************************
	 * 
	 * @param matrix
	 * @param x
	 * @param y
	 * @return zwraca wspolrzedna x ktora porzedza kolumne w kotrej warto szukac
	 */
	
	public Integer findSquare (ImageContainer ic, int x, int y, String COLOR_NAME) {
		Square square = null;
		int color = -1;
		if (rle2dUtilities.RED_NAME.equals(COLOR_NAME))
			color = ic.getRedAt(x, y);
		else if (rle2dUtilities.BLUE_NAME.equals(COLOR_NAME))
			color = ic.getBlueAt(x, y);
		else if (rle2dUtilities.GREEN_NAME.equals(COLOR_NAME))
			color = ic.getGreenAt(x, y);
		int size = 1;
		Integer blockingField = null;
		while (blockingField == null)
		{
			blockingField = getBlockingField(ic, x, y, color, size, COLOR_NAME);
			if (blockingField == null)
				size++;
		}
		int prior = 0;
		int xLen = 0;
		int yLen = 0;
		
		if(x+size>ic.getWidth())
			xLen = ic.getWidth() - x;
		else
			xLen = size;
		
		if(y+size>ic.getHeigth())
			yLen = ic.getHeigth() - y;
		else
			yLen = size;
		
		prior = xLen*yLen;
		
		if (prior > rle2dUtilities.SQUARE_MIN)
		{
			square = new Square(size,x,y,color,prior,byteSize);
			if (rle2dUtilities.RED_NAME.equals(COLOR_NAME))
				redAreas.add(square);
			else if (rle2dUtilities.BLUE_NAME.equals(COLOR_NAME))
				blueAreas.add(square);
			else if (rle2dUtilities.GREEN_NAME.equals(COLOR_NAME))
				greenAreas.add(square);
		}
		return blockingField;
	}
	/**
	 * 
	 * @param matrix
	 * @param x
	 * @param y
	 * @param COLOR_NAME
	 * @return
	 */
	
	public Integer findVSeries (ImageContainer ic,int x,int y,String COLOR_NAME) {
		VSeries vseries = null;
		
		if (ic.getHeigth()-y < rle2dUtilities.VSERIES_MIN_LEN)
			return new Integer(ic.getHeigth());
		
		int color = -1;
		
		if (rle2dUtilities.RED_NAME.equals(COLOR_NAME))
			color = ic.getRedAt(x, y);
		else if (rle2dUtilities.BLUE_NAME.equals(COLOR_NAME))
			color = ic.getBlueAt(x, y);
		else if (rle2dUtilities.GREEN_NAME.equals(COLOR_NAME))
			color = ic.getGreenAt(x, y);
		
		int size = 1;
		while (y+size<ic.getHeigth() && ic.getColorComponent(x, y+size, COLOR_NAME) == color && size < rle2dUtilities.VSERIES_MAX_LEN) {
			size++;
		}
		
		if(size > rle2dUtilities.VSERIES_MIN_LEN) {
			vseries = new VSeries(x,y,size,color,byteSize);
			if (rle2dUtilities.RED_NAME.equals(COLOR_NAME))
				redAreas.add(vseries);
			else if (rle2dUtilities.BLUE_NAME.equals(COLOR_NAME))
				blueAreas.add(vseries);
			else if (rle2dUtilities.GREEN_NAME.equals(COLOR_NAME))
				greenAreas.add(vseries);
		}
		return new Integer (y+size);
	}
	
	/**
	 * 
	 */
	public void compressImage (String COLOR_NAME){
		ArrayList<Area> allFoundAreas = null;
		ArrayList<Area> markedSquares = new ArrayList<Area>();
		ArrayList<Area> markedVSeries = new ArrayList<Area>();
		int actualProgress = getGui().getProgress();
		byte[][] bitmap = null;
		if (rle2dUtilities.RED_NAME.equals(COLOR_NAME)) {
			Collections.sort(redAreas, new AreasComparator());
			allFoundAreas = redAreas;
			bitmap = redBitmap;
		}
		else if (rle2dUtilities.GREEN_NAME.equals(COLOR_NAME)) {
			Collections.sort(greenAreas, new AreasComparator());
			allFoundAreas = greenAreas;
			bitmap = greenBitmap;
		}
		else if (rle2dUtilities.BLUE_NAME.equals(COLOR_NAME)) {
			Collections.sort(blueAreas, new AreasComparator());
			allFoundAreas = blueAreas;
			bitmap = blueBitmap;
		}
		int coveredCount = 0;
		
		int sqrCov = 0;
		int serConv = 0;
		
		for (int aInx=0;aInx<allFoundAreas.size();aInx++) {
			//System.out.println(aInx+" / "+allFoundAreas.size());
			coveredCount = 0;
			Area area = allFoundAreas.get(aInx);		
			getGui().setProgress(actualProgress+(int)((25.0/3.0)*((double)aInx/(double)allFoundAreas.size())));
			if(rle2dUtilities.SQUARE.equals(area.getKind())) {
				Square square = (Square) area;
				for(int i=square.getY();i<Math.min(square.getY()+square.getA(),ic.getHeigth());i++){
					for(int j=square.getX();j<Math.min(square.getX()+square.getA(),ic.getWidth());j++){
						if(bitmap[i][j] == 0) {
							coveredCount++;
						}
					}
				}
				if (this.priorityType == rle2dUtilities.STATIC_PRIORITY){
					if (coveredCount > rle2dUtilities.SQUARE_MIN) {
						for(int i=square.getY();i<Math.min(square.getY()+square.getA(),ic.getHeigth());i++){
							for(int j=square.getX();j<Math.min(square.getX()+square.getA(),ic.getWidth());j++){
								bitmap[i][j]=1;
								sqrCov++;
							}
						}
						markedSquares.add(square);
					}
				} else if (this.priorityType == rle2dUtilities.DYNAMIC_PRIORITY){
					if (coveredCount > rle2dUtilities.SQUARE_MIN && coveredCount == square.getPriority()) {
						for(int i=square.getY();i<Math.min(square.getY()+square.getA(),ic.getHeigth());i++){
							for(int j=square.getX();j<Math.min(square.getX()+square.getA(),ic.getWidth());j++){
								bitmap[i][j]=1;
								sqrCov++;
							}
						}
						markedSquares.add(square);
					} else if (coveredCount > rle2dUtilities.SQUARE_MIN) {
						square.setPriority(coveredCount);
						Collections.sort(allFoundAreas, new AreasComparator());
						aInx--;
					}
				}
			}
			else if (rle2dUtilities.VSERIES.equals(area.getKind())) {
				VSeries vseries = (VSeries) area;
				for (int i=vseries.getY();i<vseries.getY()+vseries.getLength();i++) {
					if (bitmap[i][vseries.getX()] == 0) {
						coveredCount++;
					}
				}
				if (this.priorityType == rle2dUtilities.STATIC_PRIORITY){
					if (coveredCount > rle2dUtilities.VSERIES_MIN_LEN ) {
						for (int i=vseries.getY();i<vseries.getY()+vseries.getLength();i++) {
							bitmap[i][vseries.getX()] = 1;
							serConv++;
						}
						markedVSeries.add(vseries);
					}
				} else if (this.priorityType == rle2dUtilities.DYNAMIC_PRIORITY){
					if (coveredCount > rle2dUtilities.VSERIES_MIN_LEN && coveredCount == vseries.getPriority()) {
						for (int i=vseries.getY();i<vseries.getY()+vseries.getLength();i++) {
							bitmap[i][vseries.getX()] = 1;
							serConv++;
						}
						markedVSeries.add(vseries);
					} else if (coveredCount > rle2dUtilities.VSERIES_MIN_LEN) {
						vseries.setPriority(coveredCount);
						Collections.sort(allFoundAreas, new AreasComparator());
						aInx--;
					}
				}
			}
		}
		
		if (rle2dUtilities.RED_NAME.equals(COLOR_NAME)) {
			redMarkedSquares = markedSquares;
			redMarkedVSeries = markedVSeries;
		}
		else if (rle2dUtilities.GREEN_NAME.equals(COLOR_NAME)) {
			greenMarkedSquares = markedSquares;
			greenMarkedVSeries = markedVSeries;
		}
		else if (rle2dUtilities.BLUE_NAME.equals(COLOR_NAME)) {
			blueMarkedSquares = markedSquares;
			blueMarkedVSeries = markedVSeries;
		}
		
		System.out.println("Pokrycie czerwone kwadraty "+sqrCov);
		System.out.println("Pokrycie czerwone pionowe linie "+serConv);
	}
	
	/**
	 * 
	 * @param areas
	 */
	public void drawRed (BufferedImage bimage){
		
		for (int aInx=0;aInx<redMarkedSquares.size();aInx++) {
			Area area = redMarkedSquares.get(aInx);		
			if(rle2dUtilities.SQUARE.equals(area.getKind())) {
				Square square = (Square) area;
				for(int i=square.getY();i<Math.min(square.getY()+square.getA(),ic.getHeigth());i++){
					for(int j=square.getX();j<Math.min(square.getX()+square.getA(),ic.getWidth());j++){
						int color = bimage.getRGB(j, i) | square.getColor() << 16;
						bimage.setRGB(j, i, color);
					}
				}
			}
		}
		for (int aInx=0;aInx<redMarkedVSeries.size();aInx++) {
			Area area = redMarkedVSeries.get(aInx);
			if (rle2dUtilities.VSERIES.equals(area.getKind())) {
				VSeries vseries = (VSeries) area;
				for (int i=vseries.getY();i<vseries.getY()+vseries.getLength();i++) {
					int color = bimage.getRGB(vseries.getX(), i) | vseries.getColor() << 16;
					bimage.setRGB(vseries.getX(),i,color);
				}
			}
		}
	}
	/**
	 * 
	 * @param areas
	 * @return
	 */
	public void drawBlue (BufferedImage bimage){
		for (int aInx=0;aInx<blueMarkedSquares.size();aInx++) {
			Area area = blueMarkedSquares.get(aInx);		
			if(rle2dUtilities.SQUARE.equals(area.getKind())) {
				Square square = (Square) area;
				for(int i=square.getY();i<Math.min(square.getY()+square.getA(),ic.getHeigth());i++){
					for(int j=square.getX();j<Math.min(square.getX()+square.getA(),ic.getWidth());j++){
						int color = bimage.getRGB(j, i) | square.getColor();
						bimage.setRGB(j, i, color);
					}
				}
			}
		}
		for (int aInx=0;aInx<blueMarkedVSeries.size();aInx++) {
			Area area = blueMarkedVSeries.get(aInx);
			VSeries vseries = (VSeries) area;
			for (int i=vseries.getY();i<vseries.getY()+vseries.getLength();i++) {
				int color = bimage.getRGB(vseries.getX(), i) | vseries.getColor();
				bimage.setRGB(vseries.getX(),i,color);
			}
		}
	}
	/**
	 * 
	 * @param areas
	 */
	public void drawGreen (BufferedImage bimage){
		for (int aInx=0;aInx<greenMarkedSquares.size();aInx++) {
			Area area = greenMarkedSquares.get(aInx);		
			if(rle2dUtilities.SQUARE.equals(area.getKind())) {
				Square square = (Square) area;
				for(int i=square.getY();i<Math.min(square.getY()+square.getA(),ic.getHeigth());i++){
					for(int j=square.getX();j<Math.min(square.getX()+square.getA(),ic.getWidth());j++){
						int color = bimage.getRGB(j, i) | square.getColor() << 8;
						bimage.setRGB(j, i, color);
					}
				}
			}
		}
		for (int aInx=0;aInx<greenMarkedVSeries.size();aInx++) {
			Area area = greenMarkedVSeries.get(aInx);
			VSeries vseries = (VSeries) area;
			for (int i=vseries.getY();i<vseries.getY()+vseries.getLength();i++) {
				int color = bimage.getRGB(vseries.getX(), i) | vseries.getColor() << 8;
				bimage.setRGB(vseries.getX(),i,color);
			}
		}
	}
	
	/**
	 * 
	 */
	public void createSequences (){
		
		redSequence.ensureCapacity(ic.getWidth()*ic.getHeigth());
		greenSequence.ensureCapacity(ic.getWidth()*ic.getHeigth());
		blueSequence.ensureCapacity(ic.getWidth()*ic.getHeigth());
		
		//redSequence.
		for(int i=0;i<ic.getHeigth();i++) {
			for(int j=0;j<ic.getWidth();j++){
				if (redBitmap[i][j] == 0)
					redSequence.add((byte)(ic.getRedAt(j, i)));
				if (blueBitmap[i][j] == 0)
					blueSequence.add((byte)(ic.getBlueAt(j, i)));
				if (greenBitmap[i][j] == 0)
					greenSequence.add((byte)(ic.getGreenAt(j, i)));					
			}
			
		}
		//STRAZNICY
		redSequence.add((byte)(-1));
		blueSequence.add((byte)(-1));
		greenSequence.add((byte)(-1));
	}
	/**
	 * 
	 */
	
	
	public ArrayList<Byte> getSequence(String COLOR_NAME){
		ArrayList<Byte> retSeq = null;
		byte[][] bitmap = null;	
		int marker = -1;
		int niezaznaczone = 0;
		int actualProgress = getGui().getProgress();
		
		if (rle2dUtilities.RED_NAME.equals(COLOR_NAME)) {
			bitmap = redBitmap;
			marker = rle2dUtilities.H_SERIES_RED_MARKER;
		}
		else if(rle2dUtilities.GREEN_NAME.equals(COLOR_NAME)) {
			bitmap = greenBitmap;
			marker = rle2dUtilities.H_SERIES_GREEN_MARKER;
		}
		else if(rle2dUtilities.BLUE_NAME.equals(COLOR_NAME)) {
			bitmap = blueBitmap;
			marker = rle2dUtilities.H_SERIES_BLUE_MARKER;
		}
		if(this.rleType == rle2dUtilities.PCX_RLE) {
			RLEPCXBuffer pcxBuff = new RLEPCXBuffer();
			pcxBuff.evalProbability(COLOR_NAME,ic,bitmap);
			
			for(int i=0;i<ic.getHeigth();i++) {
				for(int j=0;j<ic.getWidth();j++){
					getGui().setProgress(actualProgress+(int)((25/4)*(i*ic.getHeigth()+j))/(ic.getHeigth()*ic.getWidth()));
					if (bitmap[i][j] == 0) {
						if (rle2dUtilities.RED_NAME.equals(COLOR_NAME)) {
							pcxBuff.addValue(new Integer(ic.getRedAt(j, i)));
						}
						else if(rle2dUtilities.GREEN_NAME.equals(COLOR_NAME)) {
							pcxBuff.addValue(new Integer(ic.getGreenAt(j, i)));
						}
						else if(rle2dUtilities.BLUE_NAME.equals(COLOR_NAME)) {
							pcxBuff.addValue(new Integer(ic.getBlueAt(j, i)));
						}
					}
				}
			}
			
			pcxBuff.addValue(new Integer(-1));
			pcxBuff.finish();
			
			if (rle2dUtilities.RED_NAME.equals(COLOR_NAME)) {
				this.redPcxMap = pcxBuff.getRevMap();
			}
			else if(rle2dUtilities.GREEN_NAME.equals(COLOR_NAME)) {
				this.greenPcxMap = pcxBuff.getRevMap();
			}
			else if(rle2dUtilities.BLUE_NAME.equals(COLOR_NAME)) {
				this.bluePcxMap = pcxBuff.getRevMap();
			}
			
			retSeq = pcxBuff.getReturnSeq();
			
		} else {
			
			RLEBuffer buff = new RLEBuffer(marker);
			
			for(int i=0;i<ic.getHeigth();i++) {
				for(int j=0;j<ic.getWidth();j++){
					if (bitmap[i][j] == 0) {
						niezaznaczone++;
						if (rle2dUtilities.RED_NAME.equals(COLOR_NAME)) {
							buff.addValue(new Integer(ic.getRedAt(j, i)));
						}
						else if(rle2dUtilities.GREEN_NAME.equals(COLOR_NAME)) {
							buff.addValue(new Integer(ic.getGreenAt(j, i)));
						}
						else if(rle2dUtilities.BLUE_NAME.equals(COLOR_NAME)) {
							buff.addValue(new Integer(ic.getBlueAt(j, i)));
						}
					}
				}
			}
			buff.addValue(new Integer(-1));
			buff.finish();
			
			
			if (rle2dUtilities.RED_NAME.equals(COLOR_NAME)) {
				redSeriesCount = buff.getCount();
			}
			else if(rle2dUtilities.GREEN_NAME.equals(COLOR_NAME)) {
				greenSeriesCount = buff.getCount();
			}
			else if(rle2dUtilities.BLUE_NAME.equals(COLOR_NAME)) {
				blueSeriesCount = buff.getCount();
			}
			
			
			retSeq = buff.getReturnSeq();
		}
		return retSeq;
	}
	
	
	public void fillFreeGaps(String COLOR_NAME) {
		
		byte [][] bitmap = null;
		ArrayList<Byte> colorSeq = null;
		int marker = -1;
		
		if(rle2dUtilities.RED_NAME.equals(COLOR_NAME)) {
			bitmap = redBitmap;
			colorSeq = redSequence;
			marker = rle2dUtilities.H_SERIES_RED_MARKER;
		}
		else if (rle2dUtilities.BLUE_NAME.equals(COLOR_NAME)) {
			bitmap = blueBitmap;
			colorSeq = blueSequence;
			marker = rle2dUtilities.H_SERIES_BLUE_MARKER;
		}
		else if (rle2dUtilities.GREEN_NAME.equals(COLOR_NAME)) {
			bitmap = greenBitmap;
			colorSeq = greenSequence;
			marker = rle2dUtilities.H_SERIES_GREEN_MARKER;
		}
		
		Sequence seq = new Sequence(colorSeq,marker);
		for (int i=0;i<ic.getHeigth();i++) {
			for(int j=0;j<ic.getWidth();j++) {
				getGui().setProgress((int)((100/7)*(i*ic.getWidth()+j))/(ic.getHeigth()*ic.getWidth()));
				if (bitmap[i][j] == 0) {
					Byte color = seq.getNextVal();
					if (rle2dUtilities.RED_NAME.equals(COLOR_NAME))
						bimage.setRGB(j, i, bimage.getRGB(j, i)| color.intValue() << 16);
					else if (rle2dUtilities.GREEN_NAME.equals(COLOR_NAME))
						bimage.setRGB(j, i, bimage.getRGB(j, i)| color.intValue() << 8);
					else if (rle2dUtilities.BLUE_NAME.equals(COLOR_NAME))
						bimage.setRGB(j, i, bimage.getRGB(j, i)| color.intValue());
					bitmap[i][j] = 1;
				}
			}
		}
	}
	
	/*
	 * 
	 */
	public void createCompressedFile() {
		rleFile = new RLECompressedFile("./temp/huffEncode.tmp");
		rleFile.addBinary(createHeader());
		putBlocks(redMarkedSquares);
		putSeries(redMarkedVSeries);
		putBlocks(greenMarkedSquares);
		putSeries(greenMarkedVSeries);
		putBlocks(blueMarkedSquares);
		putSeries(blueMarkedVSeries);
		putSequences(redSequence);
		putSequences(greenSequence);
		putSequences(blueSequence);
		rleFile.finish();
		
		//
	}
	
	public byte[] createHeader() {
		/**
		 * 1 - Liczba czerownych kwadratow
		 * 2 - Liczba czerwonych serii pionowych
		 * 3 - Liczba zielonych kwadratow
		 * 4 - Liczba zielonych serii pionowych
		 * 5 - Liczba niebieskich kwadratow zielonych
		 * 6 - Liczba niebieskich serii pionowych 
		 */
		int countOfRedSquers = redMarkedSquares.size();
		int countOfRedVSeries = redMarkedVSeries.size();
		int countOfGreenSquers = greenMarkedSquares.size();
		int countOfGreenVSeries = greenMarkedVSeries.size();
		int countOfBlueSquers = blueMarkedSquares.size();
		int countOfBlueVSeries = blueMarkedVSeries.size();
		int redSequenceSize = redSequence.size();
		int greenSequenceSize = greenSequence.size();
		int blueSequenceSize = blueSequence.size();
		
		byte [] binary = null;
		if (this.rleType == rle2dUtilities.CUSTOM_RLE)
			binary = new byte [29];
		else 
			binary = new byte [26+3*256];
		
		binary[0] = (byte)(this.rleType);
		
		binary[1] = (byte)(ic.getWidth());
		binary[2] = (byte)(ic.getWidth() >> 8);
		
		binary[3] = (byte)(ic.getHeigth());
		binary[4] = (byte)(ic.getHeigth() >> 8);
		
		binary[5] = (byte)(countOfRedSquers);
		binary[6] = (byte)(countOfRedSquers >> 8);
			
		binary[7] = (byte)(countOfRedVSeries);
		binary[8] = (byte)(countOfRedVSeries >> 8);
			
		binary[9] = (byte)(countOfGreenSquers);
		binary[10] = (byte)(countOfGreenSquers >> 8);
			
		binary[11] = (byte)(countOfGreenVSeries);
		binary[12] = (byte)(countOfGreenVSeries >> 8);
			
		binary[13] = (byte)(countOfBlueSquers);
		binary[14] = (byte)(countOfBlueSquers >> 8);
			
		binary[15] = (byte)(countOfBlueVSeries);
		binary[16] = (byte)(countOfBlueVSeries >> 8);
		
		binary[17] = (byte)(redSequenceSize);
		binary[18] = (byte)(redSequenceSize >> 8);
		binary[19] = (byte)(redSequenceSize >> 16);
		
		binary[20] = (byte)(greenSequenceSize);
		binary[21] = (byte)(greenSequenceSize >> 8);
		binary[22] = (byte)(greenSequenceSize >> 16);
		
		binary[23] = (byte)(blueSequenceSize);
		binary[24] = (byte)(blueSequenceSize >> 8);
		binary[25] = (byte)(blueSequenceSize >> 16);
		
		if(this.rleType == rle2dUtilities.CUSTOM_RLE) {
			binary[26] = (byte)(rle2dUtilities.H_SERIES_RED_MARKER);
			binary[27] = (byte)(rle2dUtilities.H_SERIES_GREEN_MARKER);
			binary[28] = (byte)(rle2dUtilities.H_SERIES_BLUE_MARKER);
		}
		else {
			for(int i=0;i<this.redPcxMap.length;i++) {
				binary [26+i] = (byte)(redPcxMap[i]);
			}
			
			for(int i=0;i<this.greenPcxMap.length;i++) {
				binary [26+256+i] = (byte)(greenPcxMap[i]);
			}
			for(int i=0;i<this.bluePcxMap.length;i++) {
				binary [26+2*256+i] = (byte)(bluePcxMap[i]);
			}
		}
		return binary;
	}
	
	public void putBlocks(ArrayList<Area> blocks){
		for (int i=0;i<blocks.size();i++) {
			rleFile.addBinary(blocks.get(i).getBinary());
		}
	}
	
	public void putSeries(ArrayList<Area> series){
		for (int i=0;i<series.size();i++) {
			rleFile.addBinary(series.get(i).getBinary());
		}
	}
	
	public void putSequences(ArrayList<Byte> sequence){
		for (int i=0;i<sequence.size();i++) {
			byte[] binary = new byte[1];
			binary[0] = sequence.get(i).byteValue();
			rleFile.addBinary(binary);
		}
	}
	
	
	public BufferedImage createBufferedImage (){
		BufferedImage bimage = new BufferedImage(ic.getWidth(), ic.getHeigth(), BufferedImage.TYPE_INT_RGB);
		return bimage;
	}
	
	private long getArchiveFileSize (File outputFile) {
		if (outputFile != null && outputFile.exists() && outputFile.isFile()){
			return outputFile.length();
		}
		return 0;
	}
	
	public TableModel createRaport() {
		long inputLength = getGui().getInputImageFileSize();
		long outputLength = getArchiveFileSize(encoder.getOutputFile());
		long outputRle = getArchiveFileSize(rleFile.getFile());
		double bitRate = (8.0*(double)outputLength)/((double)(ic.getWidth()*ic.getHeigth()));
		double bitRateOrg = (8.0*(double)inputLength)/((double)(ic.getWidth()*ic.getHeigth()));
		double bitRateRle = (8.0*(double)outputRle)/((double)(ic.getWidth()*ic.getHeigth()));
		TableModel tm = new DefaultTableModel(new String[][] {
				{ "Szerokosc obrazka", (new Integer(this.ic.getWidth())).toString() },
				{ "Wysokosc obrazka", (new Integer(this.ic.getHeigth())).toString() },
				{ "Liczba blokow czerwonych", (new Integer(redMarkedSquares.size())).toString() }, 
				{ "Liczba blokow zielonych", (new Integer(greenMarkedSquares.size())).toString() },
				{ "Liczba blokow niebieskich", (new Integer(blueMarkedSquares.size())).toString() },
				{ "Liczba pionowych serii czerwonych", (new Integer(redMarkedVSeries.size())).toString() }, 
				{ "Liczba pionowych serii zielonych", (new Integer(greenMarkedVSeries.size())).toString() },
				{ "Liczba pionowych serii niebieskichh", (new Integer(blueMarkedVSeries.size())).toString() },
				{ "Liczba poziomych serii czerwonych", (new Integer(redSeriesCount)).toString() },
				{ "Liczba poziomych serii zielonych", (new Integer(greenSeriesCount)).toString() },
				{ "Liczba poziomych serii niebieskich", (new Integer(blueSeriesCount)).toString() },
				{ "Pozostale czerwone bajty", (new Integer(redSequence.size())).toString() },
				{ "Pozostale zielone bajty", (new Integer(greenSequence.size())).toString() },
				{ "Pozostale niebieskie bajty", (new Integer(blueSequence.size())).toString() },
				{ "Wielkosc pliku wejsciowego (Bajty)", (new Long(inputLength)).toString() },
				{ "Wielkosc pliku wyjsciowego - RLE (Bajty)", (new Long(outputRle)).toString() },
				{ "Wielkosc pliku wyjsciowego - Huffman (Bajty)", (new Long(outputLength)).toString() },
				{ "Bitrate oryginalnego pliku", (new Double(bitRateOrg)).toString() },
				{ "Bitrate po zakodowaniu figur i serii", (new Double(bitRateRle)).toString() },
				{ "Bitrate po kodowaniu Huffmana", (new Double(bitRate)).toString() }
				}, new String[] {
				"", "" });
		return tm;
	}
	
	@Override
	public void run() {
		while (!isFinished) {
			
			//initGUI();
			
			getGui().initProces();
			
			bimage = new BufferedImage(ic.getWidth(), ic.getHeigth(), BufferedImage.TYPE_INT_RGB);
			
			//RED
			
			scanMatrix(rle2dUtilities.RED_NAME);
			compressImage(rle2dUtilities.RED_NAME);
			
			BufferedImage redImg = createBufferedImage();
			drawRed(redImg);
			
			getGui().endSelectRedBlocks(redImg);
			
			//GREEN
			
			scanMatrix(rle2dUtilities.GREEN_NAME);
			compressImage(rle2dUtilities.GREEN_NAME);
			
			BufferedImage greenImg = createBufferedImage();
			drawGreen(greenImg);
			
			getGui().endSelectGreenBlocks(greenImg);
			
			//BLUE
			
			scanMatrix(rle2dUtilities.BLUE_NAME);
			compressImage(rle2dUtilities.BLUE_NAME);
			
			BufferedImage blueImg = createBufferedImage();
			drawBlue(blueImg);
			
			getGui().endSelectBlueBlocks(blueImg);
			
			//SEQUENCEz
			
			//createSequences();
			
			redSequence = getSequence(rle2dUtilities.RED_NAME);
			greenSequence = getSequence(rle2dUtilities.GREEN_NAME);
			blueSequence = getSequence(rle2dUtilities.BLUE_NAME);
			
			createCompressedFile();
			
			//File outputFile = gui.getOutputFile();
			File savableFile =  new File("./temp/"+constructOutFileName());
			if (inputFile != null){
				encoder = new HuffmanEncode(rleFile.getFile(), savableFile, useSeparateStatistics);
				encoder.encode();
				
				getGui().addReportPanel(createRaport());
				
				getGui().addSavableFinalPicture(ic.getBImage(), savableFile, constructOutFileName());
			}
			
			isFinished = true;
		}
		getGui().endEncodeHSeries();
		
	}
	
	private String constructOutFileName(){
		if (inputFile != null && inputFile.isFile()){
			String inputName = inputFile.getName();
			int ind = inputName.indexOf(".");
			if (ind != -1){
				return (inputName.substring(0, ind)+".rle2d");
			}
		}
		return "Compressec.rle2d";
	}

}
